package com.fire.activiti.activitidb.process.db;

import com.fire.activiti.activitidb.process.util.DataBaseUtil;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.util.IoUtil;
import org.activiti.engine.impl.util.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author Mr.Fire
 * @date 2021/12/21
 *
 * Activiti脚本执行
 *
 * 可按数据库类型来定义不同的脚本(语法适配)
 * @desc
 */
public class ActivitDbUpdate implements ApplicationRunner {

    private static final Logger logger = LoggerFactory.getLogger(ActivitDbUpdate.class);

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public static final List<String> SUPPRT_DB = Arrays.asList(new String[]{"db2","h2","hsql","mssql","mysql","oracle","postgres"});

    @Override
    public void run(ApplicationArguments args) throws Exception {
        execute();
    }


    public void execute() throws Exception {
        DataSource dataSource = jdbcTemplate.getDataSource();
        Connection conn = dataSource.getConnection();
        try {
            final String operation = "create";
            String component = "engine";
            String resourceName = getResourceForDbOperation(operation, component, DataBaseUtil.getDatabaseType(conn));
            executeSchemaResource(operation, component, resourceName, conn);

            component = "history";
            resourceName = getResourceForDbOperation(operation, component,  DataBaseUtil.getDatabaseType(conn));
            executeSchemaResource(operation, component, resourceName, conn);

            component = "identity";
            resourceName = getResourceForDbOperation(operation, component,  DataBaseUtil.getDatabaseType(conn));
            executeSchemaResource(operation, component, resourceName, conn);

        } finally {
            conn.close();
        }
    }

    public String getResourceForDbOperation(String operation, String component, String databaseType) {
        if(SUPPRT_DB.contains(databaseType)){
            return String.format("org/activiti/db/create/activiti.%s.%s.%s.sql", databaseType, operation, component);
        }
        //自定义脚本
        return  String.format("db/activiti.%s.%s.%s.sql", databaseType, operation, component);
    }

    public void executeSchemaResource(String operation, String component, String resourceName, Connection conn) throws Exception {
        InputStream inputStream = null;
        try {
            inputStream = ReflectUtil.getResourceAsStream(resourceName);
            if (Objects.isNull(inputStream)){
                logger.error("未找到activiti相关脚本");
            }
            execResource(inputStream, resourceName, conn);
        } finally {
            IoUtil.closeSilently(inputStream);
        }
    }

    private void execResource(InputStream inputStream, String resourceName, Connection conn) throws Exception {
        byte[] bytes = IoUtil.readInputStream(inputStream, resourceName);
        String ddlStatements = new String(bytes);
        BufferedReader reader = new BufferedReader(new StringReader(ddlStatements));
        String line = readNextTrimmedLine(reader);
        StringBuilder sqlBuilder = new StringBuilder();
        while (line != null) {
            if (line.startsWith("# ")) {
                continue;
            } else if (line.startsWith("-- ")) {
                continue;
            } else if (line.startsWith("execute java ")) {
                throw new ActivitiException("no surport script: execute java.");
            } else if (line.length() > 0) {
                if (line.endsWith(";")) {
                    sqlBuilder = addSqlStatementPiece(sqlBuilder, line.substring(0, line.length() - 1));
                    execSql(conn, sqlBuilder.toString());
                    sqlBuilder.setLength(0);
                } else {
                    sqlBuilder = addSqlStatementPiece(sqlBuilder, line);
                }
            }
            line = readNextTrimmedLine(reader);
        }
    }

    private void execSql(Connection conn, String statement) throws SQLException {
        Statement jdbcStatement = conn.createStatement();
        try {
            jdbcStatement.execute(statement);
        } catch (Exception e) {
            logger.error("脚本执行错误",e);
        } finally {
            jdbcStatement.close();
        }
    }

    private String readNextTrimmedLine(BufferedReader reader) throws IOException {
        String line = reader.readLine();
        if (line != null) {
            line = line.trim();
        }
        return line;
    }

    private StringBuilder addSqlStatementPiece(StringBuilder builder, String line) {
        if (builder.length() == 0) {
            builder.append(line);
        } else {
            builder.append(" \n" + line);
        }
        return builder;
    }


}
